<?php
/**
 * User: xuanbang.wen
 * Date: 2019/8/5
 */

namespace App\Service\Engine;

use Exception;
use RuntimeException;

/**
 * Class EngineBase
 * Notes:引擎基类
 * User: xuanbang.wen
 * Date: 2019/8/7
 * @package App\Service\Engine
 */
abstract class EngineBase {

    /**
     * 引擎名称
     * @var
     */
    protected $engine;
    /**
     * 发送给引擎的串
     * @var string
     */
    protected $sendBuf = '';
    /**
     * 引擎返回串
     * @var string
     */
    protected $receiveBuf = '';
    /**
     * 错误原因编码
     * @var int
     */
    protected $errorCode = 0;
    /**
     * 错误愿意
     * @var string
     */
    protected $errorMsg = '';

    protected $ip;
    protected $port;
    protected $sendTimeOut = 1;
    protected $receiveTimeOut = 3;
    protected $debug = false;

    public function __construct($engine) {
        $this->engine = $engine;
        $engineConfig = config('engine.' . $engine);
        if (!isset($engineConfig['ip']) || !isset($engineConfig['port'])) {
            throw new RuntimeException('引擎[' . $engine . ']未配置');
        }
        $this->ip = $engineConfig['ip'];
        $this->port = $engineConfig['port'];
        $this->sendTimeOut = isset($engineConfig['sendtimeout']) && $engineConfig['sendtimeout'] > 0 ? $engineConfig['sendtimeout'] : 1;
        $this->receiveTimeOut = isset($engineConfig['receivetimeout']) && $engineConfig['receivetimeout'] > 0 ? $engineConfig['receivetimeout'] : 3;
        $this->debug = isset($engineConfig['debug'])?$engineConfig['debug']:false;
    }

    /**
     * 根据输入参数，处理发送串
     * @param array $params
     * @return mixed
     */
    abstract protected function prepareSendBuf(array $params);

    /**
     * 引擎调用  socket处理
     * @param $sendBuf
     * @return bool
     */
    public function callEngine($sendBuf) {
        $receiveBuf = '';
        $this->sendBuf = '';
        $this->receiveBuf = '';
        if($this->debug){//QA专用，记录sendbuf串，仅供qa测试使用，线上环境请勿开启，否则日志会爆掉
             file_put_contents('/tmp/' . date('Ymd') . '.log', $sendBuf . PHP_EOL, FILE_APPEND);
        }
        $socketHandler = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        try {
            if (false === $socketHandler) {
                throw new SocketErrorException('socket创建失败');
            }
            //增加连接数回收效率
            socket_set_option($socketHandler, SOL_SOCKET, SO_LINGER, array('l_linger' => 0, 'l_onoff' => 1));
            //设置链接超时
            socket_set_option($socketHandler, SOL_SOCKET, SO_RCVTIMEO, array("sec" => $this->sendTimeOut, "usec" => 0));
            socket_set_option($socketHandler, SOL_SOCKET, SO_SNDTIMEO, array("sec" => $this->receiveTimeOut, "usec" => 0));
            if (false === socket_connect($socketHandler, $this->ip, $this->port)) {
                throw new SocketErrorException('socket服务端连接失败');
            }
            $this->sendBuf = $sendBuf;
            if (false === socket_write($socketHandler, $sendBuf, strlen($sendBuf))) {
                throw new SocketErrorException('socket服务端数据写入失败');
            }
            while ($receive = socket_read($socketHandler, 1024)) {
                $receiveBuf .= $receive;
            }
            if ($receiveBuf == '') {
                throw new NoContentException('引擎返回结果为空', -1);
            }
            $this->receiveBuf = $receiveBuf;
            socket_close($socketHandler);
            return true;
        } catch (SocketErrorException $e) {
            $this->errorCode = socket_last_error($socketHandler === false ? null : $socketHandler);
            $this->errorMsg = socket_strerror($this->errorCode);
        } catch (NoContentException $e) {
            $this->errorCode = $e->getCode();
            $this->errorMsg = $e->getMessage();
        } catch (Exception $e) {
            $this->errorCode = socket_last_error($socketHandler === false ? null : $socketHandler);
            $this->errorMsg = socket_strerror($this->errorCode);
        }
        socket_close($socketHandler);
        return false;
    }

    /**
     * 解析引擎返回结果
     * @return array
     */
    abstract protected function parseReceiveBuf();

    /**
     * 获取引擎返回完整串
     * @return string
     */
    public function getReceiveBuf() {
        return $this->receiveBuf;
    }

    /**
     * 获取发送给引擎的串
     * @return string
     */
    public function getSendBuf() {
        return $this->sendBuf;
    }

    /**
     * 获取引擎调用错误code
     * @return int
     */
    public function getErrorCode() {
        return $this->errorCode;
    }

    /**
     * 获取引擎调用错误描述
     * @return string
     */
    public function getErrorMsg() {
        return $this->errorMsg;
    }

}